package com.google.code.jwebannotation;

import java.io.IOException;
import java.util.HashMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.code.jwebannotation.annotation.HttpMethod;

/**
 * Servlet implementation class ControllerServlet
 */
public class WebAnnotationServlet extends HttpServlet {

	private Log log = LogFactory.getLog(WebAnnotationServlet.class);

	private static final long serialVersionUID = 1L;
	static HashMap<String, InternalWrapper> map;
	static InternalWrapper errorPage;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		process(HttpMethod.GET, request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		process(HttpMethod.POST, request, response);
	}

	private void process(HttpMethod type, HttpServletRequest request, HttpServletResponse response) throws IOException {

		String pathName = request.getServletPath();
		if (pathName == null || pathName.equals("")) {
			pathName = request.getPathInfo();
		}

		if (log.isTraceEnabled()) log.trace("Using path '" + pathName + "'");

		InternalWrapper mw = map.get(pathName);
		if (mw != null) {
			if (mw.getHttpMethod().equals(type) || mw.getHttpMethod().equals(HttpMethod.ALL)) {
				try {
					Object[] beans = new Object[0];
					if (mw.getBean() != null) {
						if (log.isDebugEnabled()) log.debug("Fetching bean off request for class '" + mw.getBean() + "'");
						beans = new Object[mw.getBean().size()];
						for (int i = 0; i < beans.length; i++) {
							if (log.isTraceEnabled()) log.trace("Loading bean '" + mw.getBean().get(i) + "'");
							Object form = BeanFunctions.populate(mw.getBean().get(i), request);
							beans[i] = form;
						}
					}

					if (log.isTraceEnabled()) log.trace("Invoking method '" + mw.getObjectMethod() + "'");

					String ret;
					Object[] params = new Object[beans.length + 2];
					params[0] = request;
					params[1] = response;
					for (int i = 0; i < beans.length; i++) {
						params[i + 2] = beans[i];
					}
					ret = (String) mw.getObjectMethod().invoke(mw.getObject(), params);
					
					if (log.isTraceEnabled()) log.trace("Method returned: " + ret);
					
					if (ret != null) {
						request.getRequestDispatcher(ret).forward(request, response);
					}
					return;
				} catch (Exception e) {
					if (errorPage == null) throw new RuntimeException(e);
					fireErrorHandler(request, response, e);
					return;
				}
			} else {
				log.debug("Method does not match annotation");
			}
		}
		response.sendError(404);
	}

	private void fireErrorHandler(HttpServletRequest request, HttpServletResponse response, Exception ex) {
		log.trace("Firing error handler page");
		try {
			if (log.isTraceEnabled()) log.trace("Invoking method '" + errorPage.getObjectMethod() + "'");
			String s = (String) errorPage.getObjectMethod().invoke(errorPage.getObject(), request, response, ex);
			if (s != null) {
				request.getRequestDispatcher(s).forward(request, response);
			}
		} catch (Exception e) {
			log.error("Error handler throw an exception", e);
		}

	}

}
